
parser grammar Java7Parser;

@parser::header {
package parser;
}

options {
    tokenVocab=Java7Lexer;
}

/********************************************************************************************
                          Parser section
*********************************************************************************************/

compilationUnit
    :   packageDeclaration?
        (importDeclaration
        )*
        (typeDeclaration
        )*
    ;

packageDeclaration
    :   annotations?
        PACKAGE qualifiedName
        SEMI
    ;

importDeclaration
    :   IMPORT
        (STATIC
        )?
        Identifier DOT STAR
        SEMI
    |   IMPORT
        (STATIC
        )?
        Identifier
        (DOT Identifier
        )+
        (DOT STAR
        )?
        SEMI
    ;

qualifiedImportName
    :   Identifier
        (DOT Identifier
        )*
    ;

typeDeclaration
    :   classOrInterfaceDeclaration
    |   SEMI
    ;

classOrInterfaceDeclaration
    :    classDeclaration
    |   interfaceDeclaration
    ;


modifiers
    :    modifier*
    ;

modifier
    :    annotation
    |    PUBLIC
    |    PROTECTED
    |    PRIVATE
    |    STATIC
    |    ABSTRACT
    |    FINAL
    |    NATIVE
    |    SYNCHRONIZED
    |    TRANSIENT
    |    VOLATILE
    |    STRICTFP
    ;

variableModifiers
    :   annotation* FINAL? annotation*
    ;


classDeclaration
    :   normalClassDeclaration
    |   enumDeclaration
    ;

normalClassDeclaration
    :   modifiers  CLASS Identifier
        (typeParameters
        )?
        (EXTENDS type
        )?
        (IMPLEMENTS typeList
        )?
        classBody
    ;


typeParameters
    :   LT
            typeParameter
            (COMMA typeParameter
            )*
        GT
    ;

typeParameter
    :   Identifier
        (EXTENDS typeBound
        )?
    ;


typeBound
    :   type
        (AMP type
        )*
    ;


enumDeclaration
    :   modifiers ENUM Identifier (IMPLEMENTS typeList)? enumBody
    ;


enumBody
    :    LBRACE (enumConstants)? COMMA? (enumBodyDeclarations)? RBRACE
    ;

enumConstants
    :    enumConstant (COMMA enumConstant)*
    ;

/**
 * NOTE: here differs from the javac grammar, missing TypeArguments.
 * EnumeratorDeclaration = AnnotationsOpt [TypeArguments] Identifier [ Arguments ] [ "{" ClassBody "}" ]
 */
enumConstant
    :   (annotations)? Identifier(arguments)? (classBody)?
    ;

enumBodyDeclarations
    :   SEMI
        (classBodyDeclaration
        )*
    ;

interfaceDeclaration
    :   normalInterfaceDeclaration
    |   annotationTypeDeclaration
    ;

normalInterfaceDeclaration
    :   modifiers INTERFACE Identifier (typeParameters)? (EXTENDS typeList)? interfaceBody
    ;

typeList
    :   type
        (COMMA type
        )*
    ;

classBody
    :   LBRACE
        (classBodyDeclaration
        )*
        RBRACE
    ;

interfaceBody
    :   LBRACE
        (interfaceBodyDeclaration
        )*
        RBRACE
    ;

classBodyDeclaration
    :   SEMI
    |   (STATIC
        )?
        block
    |   memberDecl
    ;

memberDecl
    :    constructorDeclaration
    |    fieldDeclaration
    |    methodDeclaration
    |    classDeclaration
    |    interfaceDeclaration
    ;

methodDeclaration
    :    modifiers typeParameters? (type | VOID) Identifier formalParameters (LBRACKET RBRACKET)* (THROWS qualifiedNameList)? (block | SEMI)
    ;

/* For constructor, return type is null, name is 'init' */
constructorDeclaration
    :    modifiers typeParameters? Identifier formalParameters (THROWS qualifiedNameList)? constructorBlock
    ;

constructorBlock
    :    LBRACE explicitConstructorInvocation? blockStatement* RBRACE
    ;

fieldDeclaration
    :   modifiers
        type
        variableDeclarator
        (COMMA variableDeclarator
        )*
        SEMI
    ;

variableDeclarator
    :   Identifier
        (LBRACKET RBRACKET
        )*
        (EQ variableInitializer
        )?
    ;

interfaceBodyDeclaration
    :   interfaceFieldDeclaration
    |   interfaceMethodDeclaration
    |   interfaceDeclaration
    |   classDeclaration
    |   SEMI
    ;

interfaceMethodDeclaration
    :    modifiers (typeParameters)? (type|VOID) Identifier formalParameters (LBRACKET RBRACKET)* (THROWS qualifiedNameList)? SEMI
    ;

/**
 * NOTE, should not use variableDeclarator here, as it doesn't necessary require
 * an initializer, while an interface field does, or judge by the returned value.
 * But this gives better diagnostic message, or antlr won't predict this rule.
 */
interfaceFieldDeclaration
    :   modifiers type variableDeclarator
        (COMMA variableDeclarator
        )*
        SEMI
    ;


type
    :   classOrInterfaceType
        (LBRACKET RBRACKET
        )*
    |   primitiveType
        (LBRACKET RBRACKET
        )*
    ;

classOrInterfaceType
    :   identifierTypeArgument (DOT identifierTypeArgument)*
    ;

identifierTypeArgument
    :    Identifier typeArguments?
    ;

primitiveType
    :   BOOLEAN
    |   CHAR
    |   BYTE
    |   SHORT
    |   INT
    |   LONG
    |   FLOAT
    |   DOUBLE
    ;

typeArguments
    :    LT typeArgument (COMMA typeArgument)* GT
    |    LT GT
    ;

typeArgument
    :   type
    |   QUES ( (EXTENDS | SUPER ) type )?
    ;

qualifiedNameList
    :   qualifiedName
        (COMMA qualifiedName
        )*
    ;

formalParameters
    :   LPAREN
        (formalParameterDecls
        )?
        RPAREN
    ;

formalParameterDecls
locals [int parameterType]
    :   ellipsisParameterDecl                               {$parameterType = 1;}
    |   normalParameterDecl (COMMA normalParameterDecl)*    {$parameterType = 2;}
    |   (normalParameterDecl COMMA)+ ellipsisParameterDecl  {$parameterType = 3;}
    ;

normalParameterDecl
    :   variableModifiers type Identifier (LBRACKET RBRACKET)*
    ;

ellipsisParameterDecl
    :   variableModifiers
        type  ELLIPSIS
        Identifier
    ;

explicitConstructorInvocation
    :   (nonWildcardTypeArguments)? (THIS|SUPER) arguments SEMI
    |   primary DOT (nonWildcardTypeArguments)? SUPER arguments SEMI
    ;

qualifiedName
    :   Identifier
        (DOT Identifier
        )*
    ;

annotations
    :   (annotation
        )+
    ;

/**
 *  Using an annotation.
 * '@' is flagged in modifier
 */
annotation
    :    markerAnnotation
    |    singleElementAnnotation
    |    normalAnnotation
    ;

markerAnnotation
    :    AT qualifiedName
    ;

singleElementAnnotation
    :    AT qualifiedName LPAREN elementValue RPAREN
    ;

normalAnnotation
    :    AT qualifiedName LPAREN elementValuePairs? RPAREN
    ;

elementValuePairs
    :   elementValuePair
        (COMMA elementValuePair
        )*
    ;

elementValuePair
    :   Identifier EQ elementValue
    ;

elementValue
    :   conditionalExpression
    |   annotation
    |   elementValueArrayInitializer
    ;

elementValueArrayInitializer
    :   LBRACE
        (elementValue
            (COMMA elementValue
            )*
        )? (COMMA)? RBRACE
    ;

/**
 * Annotation declaration.
 */
annotationTypeDeclaration
    :   modifiers AT
        INTERFACE
        Identifier
        annotationTypeBody
    ;

annotationTypeBody
    :   LBRACE
        (annotationTypeElementDeclaration
        )*
        RBRACE
    ;

/**
 * NOTE: here use interfaceFieldDeclaration for field declared inside annotation. they are syntactically the same.
 */
annotationTypeElementDeclaration
    :   annotationMethodDeclaration
    |   interfaceFieldDeclaration
    |   normalClassDeclaration
    |   normalInterfaceDeclaration
    |   enumDeclaration
    |   annotationTypeDeclaration
    |   SEMI
    ;

annotationMethodDeclaration
    :    modifiers type Identifier LPAREN RPAREN (DEFAULT elementValue)? SEMI
    ;

block
    :   LBRACE
        (blockStatement
        )*
        RBRACE
    ;

/*
staticBlock returns [JCBlock tree]
        @init {
            ListBuffer<JCStatement> stats = new ListBuffer<JCStatement>();
            int pos = ((AntlrJavacToken) $start).getStartIndex();
        }
        @after {
            $tree = T.at(pos).Block(Flags.STATIC, stats.toList());
            pu.storeEnd($tree, $stop);
            // construct a dummy static modifiers for end position
            pu.storeEnd(T.at(pos).Modifiers(Flags.STATIC,  com.sun.tools.javac.util.List.<JCAnnotation>nil()),$st);
        }
    :   st_1=STATIC LBRACE
        (blockStatement
            {
                if ($blockStatement.tree == null) {
                    stats.appendList($blockStatement.list);
                } else {
                    stats.append($blockStatement.tree);
                }
            }
        )* RBRACE
    ;
*/
blockStatement
    :   localVariableDeclarationStatement
    |   classOrInterfaceDeclaration
    |   statement
    ;


localVariableDeclarationStatement
    :   localVariableDeclaration
        SEMI
    ;

localVariableDeclaration
    :   variableModifiers type
        variableDeclarator
        (COMMA variableDeclarator
        )*
    ;

statement
    :   block
    |   ASSERT  expression (COLON expression)? SEMI
    |   IF parExpression statement (ELSE statement)?
    |   forstatement
    |   WHILE parExpression statement
    |   DO statement WHILE parExpression SEMI
    |   trystatement
    |   SWITCH parExpression LBRACE switchBlockStatementGroups RBRACE
    |   SYNCHRONIZED parExpression block
    |   RETURN (expression )? SEMI
    |   THROW expression SEMI
    |   BREAK
            (Identifier
            )? SEMI
    |   CONTINUE
            (Identifier
            )? SEMI
    |   expression  SEMI
    |   Identifier COLON statement
    |   emptyStatement
    ;

emptyStatement
    :    SEMI
    ;

switchBlockStatementGroups
    :   (switchBlockStatementGroup )*
    ;

switchBlockStatementGroup
    :
        switchLabel
        (blockStatement
        )*
    ;

switchLabel
    :   CASE expression COLON
    |   DEFAULT COLON
    ;


trystatement
locals [int statementType]
    :    TRY block                       {$statementType = 1;}
    |    TRY block catches FINALLY block {$statementType = 2;}
    |    TRY block catches               {$statementType = 3;}
    |    TRY block FINALLY block         {$statementType = 4;}
    |    tryWithResources                {$statementType = 5;}
    ;

tryWithResources
    :    TRY resourceSpecification block catches? (FINALLY block)?
    ;

resourceSpecification
    :    '(' resources (';')? ')'                                                                      // ( Resources [;] )
    ;

resources
    :    resource ( ';' resource )*                                                                // Resource { ; Resource }
    ;

resource
    :    variableModifiers? type Identifier EQ expression                       // {VariableModifier} ReferenceType VariableDeclaratorId = Expression
    ;

catches
    :   catchClause
        (catchClause
        )*
    ;

catchClause
    :   CATCH LPAREN catchFormalParameter RPAREN block
    ;

catchFormalParameter
    :    variableModifiers type (BAR type)* Identifier (LBRACKET RBRACKET)*
    ;

forstatement
    :    foreachStatement
    |    normalForStatement
    ;

foreachStatement
    :    FOR LPAREN variableModifiers type Identifier COLON expression RPAREN statement
    ;

normalForStatement
    :    FOR LPAREN (forInit)? SEMI (expression)? SEMI (expressionList)? RPAREN statement
    ;

forInit
    :   localVariableDeclaration
    |   expressionList
    ;

parExpression
    :   LPAREN expression RPAREN
    ;

expressionList
    :   expression
        (COMMA expression
        )*
    ;

expression
    :   conditionalExpression
        (assignmentOperator expression
        )?
    ;

assignmentOperator
locals [int assignmentType]
    :   EQ          {$assignmentType = 1;}
    |   PLUSEQ      {$assignmentType = 2;}
    |   SUBEQ       {$assignmentType = 3;}
    |   STAREQ      {$assignmentType = 4;}
    |   SLASHEQ     {$assignmentType = 5;}
    |   AMPEQ       {$assignmentType = 6;}
    |   BAREQ       {$assignmentType = 7;}
    |   CARETEQ     {$assignmentType = 8;}
    |   PERCENTEQ   {$assignmentType = 9;}
    |   LT LT EQ    {$assignmentType = 10;}
    |   GT GT GT EQ {$assignmentType = 11;}
    |   GT GT EQ    {$assignmentType = 12;}
    ;

conditionalExpression
    :   conditionalOrExpression
        (QUES expression COLON conditionalExpression
        )?
    ;

conditionalOrExpression
    :   conditionalAndExpression
        (BARBAR conditionalAndExpression
        )*
    ;

conditionalAndExpression
    :   inclusiveOrExpression
        (AMPAMP inclusiveOrExpression
        )*
    ;

inclusiveOrExpression
    :   exclusiveOrExpression
        (BAR exclusiveOrExpression
        )*
    ;

exclusiveOrExpression
    :   andExpression
        (CARET andExpression
        )*
    ;

andExpression
    :   equalityExpression
        (AMP equalityExpression
        )*
    ;

equalityExpression
    :   notEqualityExpression (EQEQ notEqualityExpression)*
    ;

notEqualityExpression
    :    instanceOfExpression (BANGEQ instanceOfExpression)*
    ;

instanceOfExpression
    :   relationalExpression
        (INSTANCEOF type
        )?
    ;

relationalExpression
    :   shiftExpression (relationalOp shiftExpression)*
    ;

relationalOp
    locals [int operatorType]
    :   LT EQ {$operatorType = 1;}
    |   GT EQ {$operatorType = 2;}
    |   LT    {$operatorType = 3;}
    |   GT    {$operatorType = 4;}
    ;

shiftExpression
    :   additiveExpression (shiftOp additiveExpression)*
    ;

shiftOp
    locals [int operatorType]
    :    LT LT    {$operatorType = 1;}
    |    GT GT GT {$operatorType = 2;}
    |    GT GT    {$operatorType = 3;}
    ;


additiveExpression
    :   multiplicativeExpression (additiveOp multiplicativeExpression)*
    ;

additiveOp
    locals [int operatorType]
    :    PLUS    {$operatorType = 1;}
    |    SUB     {$operatorType = 2;}
    ;

multiplicativeExpression
    :
        unaryExpression (multiplicativeOp unaryExpression)*
    ;

multiplicativeOp
    locals [int operatorType]
    :    STAR    {$operatorType = 1;}
    |    SLASH   {$operatorType = 2;}
    |    PERCENT {$operatorType = 3;}
    ;

/**
 * NOTE: for '+' and '-', if the next token is int or long interal, then it's not a unary expression.
 *       it's a literal with signed value. INTLTERAL AND LONG LITERAL are added here for this.
 */
unaryExpression
    :   PLUS  unaryExpression
    |   SUB unaryExpression
    |   PLUSPLUS unaryExpression
    |   SUBSUB unaryExpression
    |   TILDE unaryExpression
    |   BANG unaryExpression
    |   unaryExpressionNotPlusMinus
    ;

unaryExpressionNotPlusMinus
    :   castExpression
    |   primary (selector)* (PLUSPLUS | SUBSUB)?
    ;

castExpression
    :   LPAREN primitiveType RPAREN unaryExpression
    |   LPAREN type RPAREN unaryExpressionNotPlusMinus
    ;

/**
 * have to use scope here, parameter passing isn't well supported in antlr.
 */
primary
locals [int operationType]
    :    parExpression                                      {$operationType = 1;}
    |    THIS (DOT Identifier)* (thisSuffix)?               {$operationType = 2;}
    |    Identifier (DOT Identifier)* (identifierSuffix)?   {$operationType = 3;}
    |    SUPER superSuffix                                  {$operationType = 4;}
    |    literal                                            {$operationType = 5;}
    |    creator                                            {$operationType = 6;}
    |    primitiveType (LBRACKET RBRACKET)* DOT CLASS       {$operationType = 7;}
    |    VOID DOT CLASS                                     {$operationType = 8;}
    ;

superSuffix
    :   DOT (typeArguments)? Identifier (arguments)?
    ;

thisSuffix
locals [int operationType]
    :   (LBRACKET expression RBRACKET)+                     {$operationType = 1;}
    |   arguments                                           {$operationType = 2;}
    |   DOT nonWildcardTypeArguments Identifier arguments   {$operationType = 3;}
    |   innerCreator                                        {$operationType = 4;}
    ;


identifierSuffix
locals [int operationType]
    :   (LBRACKET RBRACKET)+ DOT CLASS                      {$operationType = 1;}
    |   (LBRACKET expression RBRACKET)+                     {$operationType = 2;}
    |   arguments                                           {$operationType = 3;}
    |   DOT CLASS                                           {$operationType = 4;}
    |   DOT nonWildcardTypeArguments Identifier arguments   {$operationType = 5;}
    |   DOT THIS                                            {$operationType = 6;}
    |   innerCreator                                        {$operationType = 7;}
    ;

selector
locals [int operationType]
    :   DOT Identifier (arguments)?     {$operationType = 1;}
    |   DOT THIS                        {$operationType = 2;}
    |   DOT SUPER superSuffix           {$operationType = 3;}
    |   innerCreator                    {$operationType = 4;}
    |   LBRACKET expression RBRACKET    {$operationType = 5;}
    ;

creator
    :   NEW nonWildcardTypeArguments classOrInterfaceType classCreatorRest
    |   NEW classOrInterfaceType classCreatorRest
    |   arrayCreator
    ;

arrayCreator
    :   NEW createdName LBRACKET RBRACKET (LBRACKET RBRACKET)* arrayInitializer
    |   NEW createdName LBRACKET expression RBRACKET (LBRACKET expression RBRACKET)* (LBRACKET RBRACKET)*
    ;

variableInitializer
    :   arrayInitializer
    |   expression
    ;

arrayInitializer
    :   LBRACE
            (variableInitializer
                (COMMA variableInitializer
                )*
            )?
            (COMMA)?
        RBRACE             //Yang's fix, position change.
    ;

createdName
    :   classOrInterfaceType
    |   primitiveType
    ;

innerCreator
    :   DOT NEW (nonWildcardTypeArguments)? identifierTypeArgument classCreatorRest
    ;

classCreatorRest
    :   arguments (classBody)?
    ;

nonWildcardTypeArguments
    :   LT typeList GT
    ;

arguments
    :   LPAREN (expressionList
        )? RPAREN
    ;

literal
    :   IntegerLiteral
    |   FloatingPointLiteral
    |   CharacterLiteral
    |   StringLiteral
    |   TRUE
    |   FALSE
    |   NULL
    ;

/**
 * These are headers help to make syntatical predicates, not necessary but helps to make grammar faster.
 */

classHeader
    :   modifiers CLASS Identifier
    ;

enumHeader
    :   modifiers (ENUM|Identifier) Identifier
    ;

interfaceHeader
    :   modifiers INTERFACE Identifier
    ;

annotationHeader
    :   modifiers AT INTERFACE Identifier
    ;

typeHeader
    :   modifiers (CLASS|ENUM|(AT ? INTERFACE)) Identifier
    ;

methodHeader
    :   modifiers typeParameters? (type|VOID)? Identifier RPAREN
    ;

fieldHeader
    :   modifiers type Identifier (LBRACKET RBRACKET)* (EQ|COMMA|SEMI)
    ;

localVariableHeader
    :   variableModifiers type Identifier (LBRACKET RBRACKET)* (EQ|COMMA|SEMI)
    ;
